import React, { Component } from 'react';
import { Button } from 'antd';
import styles from './index.less';

export default class ThreeDemo extends Component {
  state = { type: 'sphere', objects: [], targets: {}, id: '' };

  componentDidMount() {
    const { originalData } = this.props;
    if (originalData.length) this.init(originalData);
  }

  componentWillReceiveProps(nextProps) {
    const { originalData } = this.props;
    if (nextProps.originalData && nextProps.originalData.length && !originalData.length) {
      this.init(nextProps.originalData);
    }
  }

  // 初始化
  init = originalData => {
    const original = [];
    for (let i = 0; i < 18; i++) {
      for (let j = 0; j < 8; j++) {
        original.push({
          id: originalData[i + j * 18].id,
          name: originalData[i + j * 18].name,
          x: i + 1,
          y: j + 1,
        });
      }
    }

    const objects = [];
    const targets = { table: [], sphere: [] };

    const { width, height } = this.getSize();
    const camera = new window.THREE.PerspectiveCamera(40, width / height, 1, 10000);
    camera.position.z = 3000;

    const scene = new window.THREE.Scene();
    for (let i = 0; i < original.length; i++) {
      const element = document.createElement('div');
      element.id = original[i].id;
      element.className = i === 0 ? 'element active' : 'element';
      element.style.backgroundColor = `rgba(0,127,127,${Math.random() * 0.5 + 0.25})`;
      element.onclick = () => this.onElementClick(original[i].id);
      const symbol = document.createElement('div');
      symbol.className = 'symbol';
      symbol.textContent = original[i].name;
      element.appendChild(symbol);

      const object = new window.THREE.CSS3DObject(element);
      object.position.x = Math.random() * 4000 - 2000;
      object.position.y = Math.random() * 4000 - 2000;
      object.position.z = Math.random() * 4000 - 2000;
      scene.add(object);

      objects.push(object);

      // table

      const objectTable = new window.THREE.Object3D();
      objectTable.position.x = original[i].x * 180 - 1710;
      objectTable.position.y = -(original[i].y * 220) + 990;

      targets.table.push(objectTable);
    }

    // sphere

    const vector = new window.THREE.Vector3();

    for (let i = 0, l = objects.length; i < l; i++) {
      const phi = Math.acos(-1 + (2 * i) / l);
      const theta = Math.sqrt(l * Math.PI) * phi;

      const objectSphere = new window.THREE.Object3D();

      objectSphere.position.setFromSphericalCoords(800, phi, theta);

      vector.copy(objectSphere.position).multiplyScalar(2);

      objectSphere.lookAt(vector);

      targets.sphere.push(objectSphere);
    }

    //
    const renderer = new window.THREE.CSS3DRenderer();
    renderer.setSize(width, height);
    document.getElementById('container').appendChild(renderer.domElement);

    //
    const controls = new window.THREE.TrackballControls(camera, renderer.domElement);
    controls.rotateSpeed = 0.5;
    controls.minDistance = 500;
    controls.maxDistance = 6000;
    controls.addEventListener('change', () => this.renderThree(renderer, scene, camera));

    this.transform('sphere', targets.sphere, 2000, renderer, scene, camera, objects);

    this.animate(renderer, scene, camera, controls);

    window.addEventListener('resize', () => this.onWindowResize(renderer, scene, camera), false);

    this.setState({ targets, renderer, scene, camera, objects, id: original[0].id });
  };

  // 获取窗口尺寸
  getSize = () => {
    const { width: proWidth, height: proHeight } = this.props;
    const { clientWidth, clientHeight } = this.root ? this.root.parentNode : {};
    return { width: proWidth || clientWidth, height: proHeight || clientHeight };
  };

  // 卡片沿路径移动
  transform = (type, targets, duration, renderer, scene, camera, objects) => {
    // 初始化相机、场景
    camera.position.x = 0;
    camera.position.y = 0;
    camera.position.z = 3000;
    camera.up.x = 0;
    camera.up.y = 1;
    camera.up.z = 0;
    camera.lookAt({
      x: 0,
      y: 0,
      z: 0,
    });
    scene.rotation.y = 0;

    window.TWEEN.removeAll();

    for (let i = 0; i < objects.length; i++) {
      const object = objects[i];
      const target = targets[i];

      new window.TWEEN.Tween(object.position)
        .to(
          { x: target.position.x, y: target.position.y, z: target.position.z },
          Math.random() * duration + duration
        )
        .easing(window.TWEEN.Easing.Exponential.InOut)
        .start();

      new window.TWEEN.Tween(object.rotation)
        .to(
          { x: target.rotation.x, y: target.rotation.y, z: target.rotation.z },
          Math.random() * duration + duration
        )
        .easing(window.TWEEN.Easing.Exponential.InOut)
        .start();
    }

    new window.TWEEN.Tween(this)
      .to({}, duration * 2)
      .onUpdate(() => this.renderThree(renderer, scene, camera))
      .start();
    this.setState({ type });
  };

  // 根据窗口大小重绘
  onWindowResize = (renderer, scene, camera) => {
    const { width, height } = this.getSize();
    camera.aspect = width / height;
    camera.updateProjectionMatrix();

    renderer.setSize(width, height);

    this.renderThree(renderer, scene, camera);
  };

  // 连续动画
  animate = (renderer, scene, camera, controls) => {
    const { type } = this.state;
    // 旋转场景
    if (type === 'sphere') {
      scene.rotation.y -= 0.004;
      this.renderThree(renderer, scene, camera);
    }

    requestAnimationFrame(() => this.animate(renderer, scene, camera, controls));

    window.TWEEN.update();

    controls.update();
  };

  // 渲染画布
  renderThree = (renderer, scene, camera) => {
    renderer.render(scene, camera);
  };

  // 点击卡片
  onElementClick = id => {
    const { id: stateId, renderer, scene, camera } = this.state;
    const { onClick } = this.props;
    if (onClick) onClick(id);
    this.setState({ id });

    document.getElementById(stateId).className = 'element';
    document.getElementById(id).className = 'element active';
    this.renderThree(renderer, scene, camera);
  };

  render() {
    const { type, targets, renderer, scene, camera, objects } = this.state;
    const { width, height } = this.props;
    return (
      <div className={styles.bg} style={{ width, height }} ref={e => (this.root = e)}>
        <div className={styles.info}>
          <a href="https://threejs.org" target="_blank" rel="noopener noreferrer">
            three.js css3d
          </a>
          - periodic table.
          <a
            href="https://plus.google.com/113862800338869870683/posts/QcFk5HrWran"
            target="_blank"
            rel="noopener noreferrer"
          >
            info
          </a>
          .
        </div>
        <div id="container" />
        <div className={styles.menu}>
          {type === 'sphere' ? (
            <Button
              onClick={() =>
                this.transform('table', targets.table, 2000, renderer, scene, camera, objects)
              }
            >
              展开
            </Button>
          ) : (
            <Button
              onClick={() =>
                this.transform('sphere', targets.sphere, 2000, renderer, scene, camera, objects)
              }
            >
              收起
            </Button>
          )}
        </div>
      </div>
    );
  }
}
